#include "material.h"
#include "render_device.h"
#include "texture.h"
#include "camera.h"

const unsigned int MAX_TEXTURE_UNITS = 8; 
const char *DIFFUSEMAP_SEMANTIC = "DIFFUSEMAP";
const char *DIFFUSEMAP0_SEMANTIC = "DIFFUSEMAP0";
const char *DIFFUSEMAP1_SEMANTIC = "DIFFUSEMAP1";
const char *DIFFUSEMAP2_SEMANTIC = "DIFFUSEMAP2";
const char *DIFFUSEMAP3_SEMANTIC = "DIFFUSEMAP3";
const char *DIFFUSEMAP4_SEMANTIC = "DIFFUSEMAP4";
const char *DIFFUSEMAP5_SEMANTIC = "DIFFUSEMAP5";
const char *DIFFUSEMAP6_SEMANTIC = "DIFFUSEMAP6";
const char *DIFFUSEMAP7_SEMANTIC = "DIFFUSEMAP7";

const char *NORMALMAP0_SEMANTIC = "NORMALMAP0";

const char *WORLDVIEWPROJ_SEMANTIC = "WORLDVIEWPROJ";
const char *WORLD_SEMANTIC = "WORLD"; 
const char *WORLDINVERSETRANSPOSE = "WORLDINVERSETRANSPOSE"; 

const char *EYEPOS_SEMANTIC = "EYEPOSWORLD";

c_material::c_material(const d3dx_effect_ptr& d3dx_effect)
    : m_d3dx_effect(d3dx_effect)
{
    m_diffuse_textures.reserve(MAX_TEXTURE_UNITS);
    for (unsigned int i = 0; i < MAX_TEXTURE_UNITS; ++i)
    {
        D3DXHANDLE h = m_d3dx_effect->GetParameterBySemantic(NULL, DIFFUSEMAP_SEMANTIC);
        if (h)
            m_diffuse_textures.push_back(h);
    }
}

c_material::~c_material()
{
}

void c_material::set_world_view_proj(const c_matrix4x4& mat_world, const c_matrix4x4& mat_view, const c_matrix4x4& mat_proj)
{
    c_matrix4x4 mat_wvp = mat_world * mat_view * mat_proj;
    c_matrix4x4 mat_world_inv_transpose = c_matrix4x4::Identity();
    D3DXMatrixInverse(&mat_world_inv_transpose, NULL, &mat_world);
    D3DXMatrixTranspose(&mat_world_inv_transpose, &mat_world_inv_transpose);
    
    D3DXHANDLE h_param = m_d3dx_effect->GetParameterBySemantic(NULL, "WORLD_VIEW_PROJ");
    m_d3dx_effect->SetMatrix(h_param, (D3DXMATRIX*)&mat_wvp);
    
    h_param = m_d3dx_effect->GetParameterBySemantic(NULL, "WORLD");
    if (h_param) 
        m_d3dx_effect->SetMatrix(h_param, (D3DXMATRIX*)&mat_world);
    
    h_param  = m_d3dx_effect->GetParameterBySemantic(NULL, "WORLD_INVERSE_TRANSPOSE");
    if (h_param)
        m_d3dx_effect->SetMatrix(h_param, (D3DXMATRIX*)&mat_world_inv_transpose);
}

void c_material::set_camera_params(const camera_ptr& cam_ptr)
{
    D3DXHANDLE h_param = m_d3dx_effect->GetParameterBySemantic(NULL, EYEPOS_SEMANTIC);
    if (h_param)
    {
        c_matrix4x4 cam_world_transform = cam_ptr->calc_world_transform(); 
        c_vector4 cam_pos_world(cam_world_transform.m[3][0], cam_world_transform.m[3][1], cam_world_transform.m[3][2], 1.0f);
        m_d3dx_effect->SetVector(h_param, (D3DXVECTOR4*)&cam_pos_world);
    }
}

void c_material::set_diffuse_texture(unsigned int unit, const texture_ptr& tex)
{
    BOOST_ASSERT(unit < m_diffuse_textures.size());
    m_d3dx_effect->SetTexture(m_diffuse_textures[unit], tex->get_d3d9_texture_ptr());
}

unsigned int c_material::begin_effect()
{
    unsigned int num_passes = 0;
    D3D_VERIFY(m_d3dx_effect->Begin(&num_passes, 0)); 
    return num_passes; 
}

void c_material::end_effect()
{
    D3D_VERIFY(m_d3dx_effect->End());
}

void c_material::begin_pass(const unsigned int pass)
{
    D3D_VERIFY(m_d3dx_effect->BeginPass(pass));
}

void c_material::end_pass()
{
    D3D_VERIFY(m_d3dx_effect->EndPass());
}

///////////////////////////////////////////////////////////////////////////////////////////////////

material_ptr create_material_from_effect_file(const std::string& file_name)
{
    HRESULT hr;
    d3dx_effect_ptr d3dx_effect;
    d3dx_buffer_ptr d3dx_buffer;
    d3d9_device_ptr d3d9_device = c_render_device9::get_singleton().get_d3d_device_ptr();

    V(D3DXCreateEffectFromFileA(
        d3d9_device,
        file_name.c_str(),
        NULL,
        NULL,
        // D3DXSHADER_DEBUG | D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT | D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT,
        D3DXSHADER_DEBUG,
        NULL,
        &d3dx_effect, 
        &d3dx_buffer
        )); 

    material_ptr material = material_ptr(new c_material(d3dx_effect)); 
    
    return material; 
}

material_ptr make_default_material()
{
	static material_ptr default_material = 
		create_material_from_effect_file("data/materials/scripts/default.fx");
		
	return default_material;
}